#ifndef __PROPOSER__H__
#define __PROPOSER__H__
#include <string>
#include <sstream>
#include "message.h"
#include "network.h"

class Proposer {
private:
    int id;
    int seq;
    int proposeNum;
    std::string proposeVal;
    std::unordered_map<int, Message> acceptors;
    std::shared_ptr<Network> net;

public:
    Proposer(int id, const std::string &val,
             const std::shared_ptr<Network> &net,
             const std::initializer_list<int> &list)
        : id(id), seq(0), proposeNum(0), proposeVal(val), net(net) {
        for (auto ele : list) {
            acceptors[ele] = Message();
        }
    }
    void run(void) {
        for (; !majorityReached();) {
            std::vector<Message> msgVec = prepare();
            for (auto &msg : msgVec) {
                int ret = net->sendTcpMsg(msg);
                std::stringstream ss;
                ss << "Proposer send Prepare " << ret << ", " << msg << "\n";
                std::cout << ss.str();
            }
            std::vector<Message> msgVec1;
            int ret = net->recvTcpMsg(msgVec1);
            if (ret <= 0) {
                std::stringstream ss;
                ss << "Proposer: no msg..."
                   << "\n";
                std::cout << ss.str();
                continue;
            }
            for (auto &msg : msgVec1) {
                if (msg.typ == Message::msgType::Promise) {
                    std::stringstream ss;
                    ss << "Proposer recev Promise " << ret << "," << msg
                       << "\n";
                    std::cout << ss.str();
                    checkRecvPromise(msg);
                } else {
                    std::stringstream ss;
                    ss << std::string(__func__) << " unsupport message " << msg
                       << "\n";
                }
            }
        }
        std::stringstream ss;
        ss << "#####################" << proposeVal << "####################\n";
        std::cout << ss.str();
        std::vector<Message> proposeMsgs = propose();
        for (auto &proposeMsg : proposeMsgs) {
            net->sendTcpMsg(proposeMsg);
        }
    }
    std::vector<Message> propose() {
        int sendMsgCount = 0;
        std::vector<Message> msgVec;
        for (auto &acceptor : acceptors) {
            if (acceptor.second.seq == getProposeNum()) {
                msgVec.emplace_back(Message{Message::msgType::Propose, id,
                                            acceptor.first, getProposeNum(), 0,
                                            proposeVal});
            }
            sendMsgCount++;
            if (sendMsgCount > majority()) {
                break;
            }
        }
        return msgVec;
    }
    std::vector<Message> prepare() {
        int sendMsgCount = 0;
        std::vector<Message> msgVec;
        seq++;
        for (auto &acceptor : acceptors) {
            msgVec.emplace_back(Message{Message::msgType::Prepare, id,
                                        acceptor.first, getProposeNum(), 0,
                                        proposeVal});
            sendMsgCount++;
            if (sendMsgCount > majority()) {
                break;
            }
        }
        return msgVec;
    }
    void checkRecvPromise(Message &promise) {
        if (acceptors.find(promise.from) == acceptors.end()) {
            std::cout << "error, no such neighbor " << promise.from
                      << std::endl;
            return;
        }
        Message previousPromise = acceptors[promise.from];
        if (previousPromise.seq < promise.seq) {
            acceptors[promise.from] = promise;
            if (promise.seq > getProposeNum()) {
                proposeNum = promise.seq;
                proposeVal = promise.val;
            }
        }
    }
    int majority() { return acceptors.size() / 2 + 1; }
    int getRecevPromiseCount() {
        int recvCount = 0;
        Message msg;
        for (auto &acceptor : acceptors) {
            if (acceptor.second.seq == getProposeNum()) {
                msg = acceptor.second;
                recvCount++;
            }
        }
        return recvCount;
    }
    bool majorityReached() { return getRecevPromiseCount() > majority(); }
    int getProposeNum() {
        proposeNum = seq << 12 | id;
        return proposeNum;
    }
};

#endif